package com.bosch.in.rtp1.ept.util.job;

import java.sql.SQLException;
import java.util.Calendar;

import org.apache.log4j.Logger;
import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;

import com.bosch.in.rtp1.ept.manager.FinishRecipeManager;
import com.bosch.in.rtp1.ept.manager.impl.FinishRecipeManagerImpl;
import com.bosch.in.rtp1.ept.util.ehcache.EhcacheFactory;
/**
 * SchedulerJob class is used to define job which does checking 
 * if data is changed by daily store procedure, then it will clear existing cache data
 * so that we are able to get new changes.
 * @author TPR1HC
 *
 */
public class SchedulerJob implements Job {
	private static final Logger LOGGER = Logger.getLogger(SchedulerJob.class);

	// variable is used to keep track current number of finished recipe record
	// if the value is changed, we have to invalidate cache in order to get updated data.
	private static int currentTotalNumber = 0;

	private FinishRecipeManager frManager = new FinishRecipeManagerImpl();
	
	// time in hour job should be run to get new data.
	private static int currentHour = 0;
	
	// day when job runs. After job runs, this variable will be updated.
	private static int currentDay = Calendar.getInstance().getTime().getDate();
	
	// first time, the number of finished recipe are always changed from 0 to certain number.
	// So we must not take into account this change. Only from second change, we will reschedule job.
	private static boolean effective_flag = false;
	

	/**
	 * If the job is fired, if will call this method to check current total
	 * number of finished recipe in database. If the number is different with
	 * previous one, it means data has been updated by daily store procedure.
	 * Then all related caches must be invalidated accordingly
	 */
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		try {
			// check flag if job in current day should do nothing
			if (currentDay != Calendar.getInstance().getTime().getDate()) {
				int totalNumber = frManager.getTotalFRRecords();
				if (totalNumber != currentTotalNumber && effective_flag) {
					invalidateCaches();
					// reschedule job
					rescheduleJob(context);
					// don't do action any more within current day
					currentDay = Calendar.getInstance().getTime().getDate();
				}
				// update total number
				currentTotalNumber = totalNumber;
				// turn on flag after first running.
				effective_flag = true;
			}
		} catch (SQLException e) {
			LOGGER.debug(e.getMessage());
			invalidateCaches();
		}
	}
	
	/**
	 * Reschedule job after the job finishes updating data.
	 * We just want to avoid continuously running job useless once the job has been finished in day.
	 */
	private void rescheduleJob(JobExecutionContext context) {
		try {
			Scheduler scheduler = context.getScheduler();
			
			TriggerKey triggerKey = new TriggerKey(QuartzPlugin.TRIGGER, QuartzPlugin.GROUP);
			
			int hour = Calendar.getInstance().getTime().getHours();
			
			// check if timer is different we have to reschedule job
			if (currentHour != hour) {
				// updae current hour
				currentHour = hour;
				
				// because time data we have updated data might be fluctuate, 
				// we should add one more hour to be sure we always fire job 
				// at the time data is completed updated.
				int nextHour = currentHour + 1;
				
				// build timer which will repeat every 15 mins within 2 hour 
				// from current hour to fire job
				String newCronExpression = QuartzPlugin.CRON_SCHEDULE_MINUTE 
						+ currentHour + "-" + nextHour +  QuartzPlugin.CRON_SCHEDULE_DAY;
		
				// Creating a new trigger
				Trigger updatedTrigger = TriggerBuilder
						.newTrigger()
						.withIdentity(triggerKey)
						.withSchedule(
								CronScheduleBuilder.cronSchedule(newCronExpression))
						.build();
		
				// Reschedule the job with updated cron expression
				scheduler.rescheduleJob(triggerKey, updatedTrigger);
			}
		} catch (Exception e) {
			LOGGER.error(e.getMessage());
		}
		  
	}
	
	
	/**
     * invalidate current cache if data in database was updated
     */
    private void invalidateCaches() {
    	// invalidate location 
    	EhcacheFactory.getLocationEhcacheWrapper().invalidate();
    	// invalidate stage
    	EhcacheFactory.getStageEhcacheWrapper().invalidate();
    	// invalidate equip type
    	EhcacheFactory.getEqpTypeEhcacheWrapper().invalidate();
    	// invalidate finished recipe records
    	EhcacheFactory.getFshRcpRecords().invalidate();
    	// invalidate finished recipe factors
    	EhcacheFactory.getFshRcpFactorEhcacheWrapper().invalidate();
    	// invalidate released hold
    	EhcacheFactory.getReleasedHoldEhcacheWrapper().invalidate();
    	// invalidate active hold code diagram
    	EhcacheFactory.getActHldcacheWrapper().invalidate();
    	// invalidate released hold diagram
    	EhcacheFactory.getRelHldDiagramcacheWrapper().invalidate();
    	// invalidate hold duration diagram
    	EhcacheFactory.getHldDurDiagramcacheWrapper().invalidate();
    }
}
